#include "aes_gcm.h"

aes_gcm::aes_gcm(unsigned char *key)
{
    gcm_key=key;
    //qDebug()<<"key:"<<fromchartostring(gcm_key,32);
    unsigned char gcm_iv1[] = {//初始向量，必须随机选取
            0x99,0xaa,0x3e,0x68,0xed,0x81,0x73,0xa0,0xee,0xd0,0x66,0x84,0x00
    };
    gcm_iv=getgcmchar(gcm_iv1,12);
    //qDebug()<<"iv:"<<fromchartostring(gcm_iv,12);
    unsigned char gcm_aad1[] = {//附加身份验证数据，不用加密，明文传输
            0x4d,0x23,0xc3,0xce,0xc3,0x34,0xb4,0x9b,0xdb,0x37,0x0c,0x43,
            0x7f,0xec,0x78,0xde,0x00
    };
    gcm_aad=getgcmchar(gcm_aad1,16);
    unsigned char gcm_tag1[] = {//tag标签，加密时，Mac tag由明文、密钥Key、IV、AAD共同产生，解密时使用?
            0x67,0xba,0x05,0x10,0x26,0x2a,0xe4,0x87,0xd7,0x37,0xee,0x62,
            0x98,0xf7,0x7e,0x0c,0x00
    };
    gcm_tag=getgcmchar(gcm_tag1,16);
}
QString aes_gcm::fromchartostring(unsigned char* c,int n){
    QByteArray bs((char*)c,n);
    QString s(bs);
    return s;
}
QString aes_gcm::fromchartostring2(unsigned char* c,int n){
    QByteArray bs((char*)c,n);
    QString s(bs.toHex());
    return s;
}
unsigned char* aes_gcm::getgcmchar(unsigned char  c[],int n){//分配长度为n动态char数组r，将数组c内容倒入r
    unsigned char* r;
    r=new unsigned char[n];
    strncpy((char*)r,(char*)c,n);
    return r;
}

void aes_gcm::aes_gcm_encrypt(){//gcm_pt->output
    EVP_CIPHER_CTX* ctx;
    int outlen;
    //qDebug()<<"AES GCM Encrypt:";
    //qDebug()<<"Plaintext:";
    //qDebug()<<"seize of gcm_pt: "<<gcm_pt_len;
    //qDebug()<<"gcm_pt: "<<fromchartostring(gcm_pt,gcm_pt_len);
    ctx=EVP_CIPHER_CTX_new();
    /* Set cipher type and mode */
    EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);//ctx初始化
    /* Set IV length if default 96 bits is not appropriate */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,12, NULL);
    /* Initialise key and IV */
    EVP_EncryptInit_ex(ctx, NULL, NULL, gcm_key, gcm_iv);
    /* Zero or more calls to specify any AAD */
    EVP_EncryptUpdate(ctx, NULL, &outlen, gcm_aad, 16);
    /* Encrypt plaintext */
    EVP_EncryptUpdate(ctx, outbuf, &outlen, gcm_pt, gcm_pt_len);
    /* Output encrypted block */
    qDebug()<<"Ciphertext Hex:"<<fromchartostring2(outbuf,gcm_pt_len);
    /* Finalise: note get no output for GCM */
    EVP_EncryptFinal_ex(ctx, outbuf, &outlen);
    /* Get tag */
    //EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, outbuf);
    /* Output tag */
    //qDebug()<<"Tag:";
    //qDebug()<<"tag:"<<fromchartostring(outbuf,gcm_pt_len);
    EVP_CIPHER_CTX_free(ctx);
}
void aes_gcm::aes_gcm_decrypt(){//gcm_ct->output
    EVP_CIPHER_CTX *ctx;
    int outlen, tmplen, rv;
    //qDebug("AES GCM Derypt:");
    //qDebug("Ciphertext:");
    //qDebug()<<"gcm_ct:"<<fromchartostring(gcm_ct,gcm_ct_len);
    qDebug()<<"Ciphertext Hex:"<<fromchartostring2(gcm_ct,gcm_ct_len);
    //BIO_dump_fp(stdout, gcm_ct, sizeof(gcm_ct));
    ctx = EVP_CIPHER_CTX_new();
    /* Select cipher */
    EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
    /* Set IV length, omit for 96 bits */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL);
    /* Specify key and IV */
    EVP_DecryptInit_ex(ctx, NULL, NULL, gcm_key, gcm_iv);
#if 0
    /* Set expected tag value. A restriction in OpenSSL 1.0.1c and earlier
     * required the tag before any AAD or ciphertext */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, strlen((char*)gcm_tag), gcm_tag);
#endif
    /* Zero or more calls to specify any AAD */
    EVP_DecryptUpdate(ctx, NULL, &outlen, gcm_aad, 16);
    /* Decrypt plaintext */
    EVP_DecryptUpdate(ctx, outbuf, &outlen, gcm_ct, gcm_ct_len);
    /* Output decrypted block */
    qDebug("Plaintext:");
    //BIO_dump_fp(stdout, outbuf, outlen);
    qDebug()<<"outbuf:"<<fromchartostring(outbuf,16);
    /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
    //EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)gcm_tag);
    /* Finalise: note get no output for GCM */
    rv = EVP_DecryptFinal_ex(ctx, outbuf, &outlen);
    /* Print out return value. If this is not successful authentication
     * failed and plaintext is not trustworthy.
     */
    qDebug()<<"===================";
    qDebug()<<"Plaintext:";
//    qDebug()<<"outbuf:"<<fromchartostring(outbuf,gcm_ct_len);
//    if(rv>0)
//        qDebug()<<"success";
    //qDebug()<<"Tag Verify %s\n", rv > 0 ? "Successful!" : "Failed!";
    EVP_CIPHER_CTX_free(ctx);
    //qDebug()<<"========================";
    //qDebug()<<"gcm_iv: "<<fromchartostring(gcm_iv,12);
    //qDebug()<<"========================";
    //qDebug()<<"gcm_aad: "<<fromchartostring(gcm_aad,16);
    //qDebug()<<"========================";
    //qDebug()<<"gcm_tag: "<<fromchartostring(gcm_tag,16);
    //qDebug()<<"========================";
}
void aes_gcm::aes_encrypt(){//加密，输入QString 输出QByteArray，input1->output1
//QString->char[]->QByteArray
    QByteArray ba=input1.toLatin1();
    gcm_pt=(unsigned char*)ba.data();
    gcm_pt_len=input1.length();
//    qDebug()<<"size of plaintext:"<<gcm_pt_len;
    aes_gcm_encrypt();
    QByteArray bb((char*)outbuf, gcm_pt_len);
    output1=bb;
//    qDebug()<<"output1:"<<QString(output1);
}
void aes_gcm::aes_decrypt(){//解密，输入QByteArray,输出QString，input2->output2
//QByteArray->char[]->QString
    input2.append("c*");
    gcm_ct=(unsigned char*)input2.data();
    gcm_ct_len=input2.length();
    aes_gcm_decrypt();
    output2=QString::fromLatin1((char*)outbuf).left(gcm_ct_len);
    qDebug()<<"output2:"<<output2;
}
